package com.zhj.annotation.processor.handler;

import com.google.auto.service.AutoService;
import com.google.common.collect.Sets;

import com.zhj.annotation.processor.AbstractAnnotationProcessor;
import com.zhj.annotation.processor.util.ProcessorUtils;

import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.SortedSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;

import javax.annotation.processing.Filer;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.Element;
import javax.lang.model.element.QualifiedNameable;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;
import javax.tools.FileObject;
import javax.tools.StandardLocation;


@AutoService(Processor.class)
@SupportedAnnotationTypes({SpiAnnotationProcessor.SPI_NAME,
                           SpiAnnotationProcessor.COMPONENT_NAME})
public class SpiAnnotationProcessor extends AbstractAnnotationProcessor {

    /** 注解 @SPI */
    static final String SPI_NAME = "com.zhj.annotation.processor.SPI";
    /** 注解 @SpiService */
    static final String COMPONENT_NAME = "com.zhj.annotation.processor.SPIService";
    /** 结构化 Map 存储编排后的数据 */
    private final Map<Element, List<Element>> interfaceAndSubClazz = new ConcurrentHashMap<>();
    /** 文件生成路径 */
    static final String PATH = "META-INF/spiservices/";

    private RoundEnvironment roundEnv;

    @Override
    protected boolean processImpl(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        this.roundEnv = roundEnv;
        //判断是否是最后一轮
        if (roundEnv.processingOver()) {
            logInfo("================================= 输出文件路径开始 =======================================");
            this.writeFile();
            logInfo("================================= 输出文件路径结束 =======================================");
        } else {
            processorAnnotation(annotations);
        }
        return true;
    }

    private void processorAnnotation(Set<? extends TypeElement> annotations) {
        this.processIfPresent(annotations, SPI_NAME, elementsAnnotatedWith -> {
            //先把spi注解写入到 map当中 存储起来
            elementsAnnotatedWith.forEach(element -> {
                this.interfaceAndSubClazz.put(element, new ArrayList<>());
            });
            //在获取 SPIService 注解标注的类
            this.processIfPresent(annotations, COMPONENT_NAME, componentElement -> {
                Set<Element> superElements = this.interfaceAndSubClazz.keySet();
                for (Element superElement : superElements) {
                    logInfo("子类个数：" + componentElement.size());
                    for (Element component : componentElement) {
                        TypeMirror erasure = this.typeUtils.erasure(superElement.asType());
                        this.addSubClazz(superElement, component, erasure);
                    }
                }
            });
        });
    }

    private void addSubClazz(Element superElement, Element component, TypeMirror erasure) {
        //判断是否是当前接口的子类
        logInfo("判断当前类是否是接口类型：" + this.typeUtils.isSubtype(component.asType(), erasure));
        if (this.typeUtils.isSubtype(component.asType(), erasure)) {
            this.interfaceAndSubClazz.get(superElement).add(component);
        }
    }

    /**
     * 遍历获取对应注解的类
     * @param annotations
     * @param annotationName
     * @param func
     */
    private void processIfPresent(Set<? extends TypeElement> annotations,
                                  String annotationName,
                                  Consumer<Set<? extends Element>> func) {
        //先获取到是否包含指定注解
        Optional<? extends TypeElement> typeElement =
            annotations.stream().filter(annotation -> annotation.getQualifiedName().contentEquals(annotationName)).findFirst();

        //如果注解存在获取到注解标注的所有类
        if (typeElement.isPresent()) {
            Set<? extends Element> elementsAnnotatedWith = this.roundEnv.getElementsAnnotatedWith(typeElement.get());
            logInfo("找到注解：" + annotationName + "个数：" + elementsAnnotatedWith.size());
            func.accept(elementsAnnotatedWith);
        }
    }

    private void writeFile() {
        Filer filer = this.filer;
        // IO 写入，不同key写进不同文件，所以异步加快
        this.interfaceAndSubClazz.entrySet().parallelStream().forEach(entry -> {
            String resourceFile = PATH + this.getQualifiedName(entry.getKey());
            try {
                //获取到当前文件中所有的数据，并且每次读取一行对象
                SortedSet<String> allServices = ProcessorUtils.getAllServices(filer, resourceFile);
                //构建当前接口下面子类的名称
                Set<String> newServices = this.buildServices(entry.getValue());
                //如果都有了就不需要在执行写入
                if (allServices.containsAll(newServices)) {
                    return;
                }
                allServices.addAll(newServices);
                this.writeFile(filer, resourceFile, allServices, StandardLocation.CLASS_OUTPUT);

            } catch (IOException e) {
                this.errorInfo(e.getMessage());
            }
        });
        this.interfaceAndSubClazz.clear();
    }

    private void writeFile(Filer filer, String resourceFile, SortedSet<String> allServices, StandardLocation... locations)
        throws IOException {
        for (StandardLocation location : locations) {
            FileObject sourceOut = filer.createResource(location, "", resourceFile);
            OutputStream out = sourceOut.openOutputStream();
            ProcessorUtils.writeServiceFile(allServices, out);
            out.close();
            this.logInfo("Wrote to: " + sourceOut.toUri());
        }

    }

    /**
     * 构建出类的名称
     * @param subElements
     * @return
     */
    private Set<String> buildServices(List<Element> subElements) {
        Set<String> eq = Sets.newHashSet();
        for (Element subElement : subElements) {
            List<? extends AnnotationMirror> annotationMirrors = subElement.getAnnotationMirrors();
            //获取到对象的@SpiService 注解
            Optional<? extends AnnotationMirror> mirror =
                annotationMirrors.stream().filter(m -> m.getAnnotationType().toString().equals(COMPONENT_NAME)).findFirst();
            if (mirror.isPresent()) {
                //通过注解获取到 @SpiService 中的属性值
                AnnotationMirror annotationMirror = mirror.get();
                eq.add(String.format("%s=%s", this.getValue(subElement, annotationMirror), this.getQualifiedName(subElement)));
            }
        }
        return eq;
    }

    /**
     * 构建类的名称
     * @param element
     * @return
     */
    protected String getQualifiedName(Element element) {
        if (element instanceof QualifiedNameable) {
            return ((QualifiedNameable) element).getQualifiedName().toString();
        }
        return element.toString();
    }

    /**
     * 获取到对应的value值
     * @param element
     * @param annotationMirror
     * @return
     */
    private String getValue(Element element, AnnotationMirror annotationMirror) {
        AnnotationValue value = this.getAnnotationValue(annotationMirror, "value");
        String val = value.toString();
        if (val == null || "\"\"".equals(val)) {
            String camel = element.getSimpleName().toString();
            return camel.substring(0, 1).toLowerCase() + camel.substring(1);
        }
        // formatter \"\"
        return val.substring(1, val.length() - 1);
    }
}
